package layer.versioning;

import lights.interfaces.IField;
import lights.interfaces.ITuple;
import lime.AgentLocation;
import lime.IllegalTupleSpaceNameException;
import lime.LimeTupleID;
import lime.LimeTupleSpace;
import lime.Location;
import lime.TupleSpaceEngineException;

public class VersionedLimeTupleSpace {
	
	private static long n = 0;
	private LimeTupleSpace limeTupleSpace;
	
	public VersionedLimeTupleSpace()
		throws TupleSpaceEngineException, IllegalTupleSpaceNameException {
		limeTupleSpace = new LimeTupleSpace();
	}
	
	public VersionedLimeTupleSpace(String name)
			throws TupleSpaceEngineException, IllegalTupleSpaceNameException {
		limeTupleSpace = new LimeTupleSpace(name);
	}
	
	public VersionedTuple change(ITuple template, ITuple t) throws TupleSpaceEngineException{
		VersionedTuple versionedTuple = this.rd(template);
		versionedTuple.updateVersion();
		ITuple tuple = versionedTuple.getTuple();
		// replicable
		if (tuple.get(tuple.length()-1).getType() == AgentLocation.class){
			IField origDest = tuple.get(tuple.length()-1);
			IField origCur = tuple.get(tuple.length()-2);
			IField isReplica = tuple.get(tuple.length()-3);
			t.add(isReplica).add(origCur).add(origDest);
			versionedTuple.setTuple(t);
		}
		// versioned
		else{
			versionedTuple.setTuple(t);
		}
		this.out(versionedTuple);
		return versionedTuple;
	}
	
	public String getName(){
		return limeTupleSpace.getName();
	}
	
	public VersionedTuple in(ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuple = limeTupleSpace.in(template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		return new VersionedTuple(tuple);
	}
	
	public VersionedTuple in(Location current, AgentLocation destination, ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuple = limeTupleSpace.in(current, destination, template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		return new VersionedTuple(tuple);
	}
	
	public VersionedTuple[] ing(Location current, AgentLocation destination, ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuples[] = limeTupleSpace.ing(current, destination, template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		VersionedTuple[] versionedTuples = new VersionedTuple[tuples.length];
		for (int i=0;i<tuples.length;i++)
			versionedTuples[i] = new VersionedTuple(tuples[i]);
		return versionedTuples;
	}
	
	public VersionedTuple inp(Location current, AgentLocation destination, ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuple = limeTupleSpace.inp(current, destination, template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		return new VersionedTuple(tuple);
	}
	
	public boolean isOwner(){
		return limeTupleSpace.isOwner();
	}
	
	public boolean isShared(){
		return limeTupleSpace.isShared();
	}
	
	public void out(AgentLocation destination, ITuple tuple)
	throws TupleSpaceEngineException {
		VersionedTuple versionedTuple = new VersionedTuple(n++);
		versionedTuple.setTuple(tuple);
		// <Tuple> -> <Tuple, versNum, tupleID>
		tuple.addActual(versionedTuple.getTupleVersion());
		tuple.addActual(versionedTuple.getTupleID());
		limeTupleSpace.out(destination, tuple);
	}
	
	public void out(ITuple tuple) throws TupleSpaceEngineException {
		VersionedTuple versionedTuple = new VersionedTuple(n++);
		versionedTuple.setTuple(tuple);
		// <Tuple> -> <Tuple, versNum, tupleID>
		tuple.addActual(versionedTuple.getTupleVersion());
		tuple.addActual(versionedTuple.getTupleID());
		limeTupleSpace.out(tuple);
		tuple = tuple.removeAt(tuple.length()-1);
		tuple = tuple.removeAt(tuple.length()-1);
	}
	
	private void out(VersionedTuple versionedTuple) throws TupleSpaceEngineException{
		ITuple tuple = versionedTuple.getTuple();
		tuple.addActual(versionedTuple.getTupleVersion());
		tuple.addActual(versionedTuple.getTupleID());
		limeTupleSpace.out(tuple);
		tuple = tuple.removeAt(tuple.length()-1);
		tuple = tuple.removeAt(tuple.length()-1);
	}
	
	public void outg(AgentLocation destination, ITuple[] tuples)
	throws TupleSpaceEngineException {
		for (ITuple tuple : tuples){
			VersionedTuple versionedTuple = new VersionedTuple(n++);
			versionedTuple.setTuple(tuple);
			// <Tuple> -> <Tuple, versNum, tupleID>
			tuple.addActual(versionedTuple.getTupleVersion());
			tuple.addActual(versionedTuple.getTupleID());
		}
		limeTupleSpace.outg(destination, tuples);
		for (ITuple tuple : tuples){
			tuple = tuple.removeAt(tuple.length()-1);
			tuple = tuple.removeAt(tuple.length()-1);
		}
	}
	
	public void print(){
		limeTupleSpace.print();
	}
	
	public VersionedTuple rd(ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuple = limeTupleSpace.rd(template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		return new VersionedTuple(tuple);
	}
	
	public VersionedTuple rd(Location current, AgentLocation destination, ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuple = limeTupleSpace.rd(current, destination, template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		return new VersionedTuple(tuple);
	}
	
	public VersionedTuple[] rdg(Location current, AgentLocation destination, ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple[] tuples = limeTupleSpace.rdg(current, destination, template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		VersionedTuple[] versionedTuples = new VersionedTuple[tuples.length];
		for (int i=0;i<tuples.length;i++)
			versionedTuples[i] = new VersionedTuple(tuples[i]);
		return versionedTuples;
	}
	
	public VersionedTuple rdp(Location current, AgentLocation destination, ITuple template) throws TupleSpaceEngineException{
		template.addFormal(LimeTupleVersion.class).addFormal(LimeTupleID.class);
		ITuple tuple = limeTupleSpace.rdp(current, destination, template);
		template.removeAt(template.length()-1);
		template.removeAt(template.length()-1);
		return new VersionedTuple(tuple);
	}
	
	public boolean setShared(boolean isShared){
		return limeTupleSpace.setShared(isShared);
	}
}
